//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

// DISCLAIMER: THIS PLUGIN IS PROVIDED AS IS.  IT IS NOT SUPPORTED BY

//            	AUTODESK, SO PLEASE USE AND MODIFY AT YOUR OWN RISK.

//

// PLUGIN NAME: closestPointOnCurve v1.0

// FILE: closestPointOnCurveCmd.cpp

// DESCRIPTION: -Defines `closestPointOnCurve` MEL command.

//              -Please see readme.txt for full details.

// AUTHOR: QT

// REFERENCES: -This plugin's concept is based off of the "closestPointOnSurface" node.

//             -The MEL script AEclosestPointOnSurfaceTemplate.mel was referred to for

//              the AE template MEL script that accompanies the closestPointOnCurve node.

// LAST UPDATED: Oct. 13th, 2001.

// COMPILED AND TESTED ON: Maya 4.0 on Windows





// HEADER FILES:

#include "closestPointOnCurveCmd.h"

#include "closestTangentUAndDistance.h"





// CONSTRUCTOR DEFINITION:

closestPointOnCurveCommand::closestPointOnCurveCommand()

{

}





// DESTRUCTOR DEFINITION:

closestPointOnCurveCommand::~closestPointOnCurveCommand()

{

}





// METHOD FOR CREATING AN INSTANCE OF THIS COMMAND:

void *closestPointOnCurveCommand::creator()

{

   return new closestPointOnCurveCommand;

}





// CREATES THE SYNTAX OBJECT FOR THE COMMAND:

MSyntax closestPointOnCurveCommand::newSyntax()

{

   MSyntax syntax;



   // INPUT FLAGS:

   syntax.addFlag("-na", "-name", MSyntax::kString);

   syntax.addFlag("-ip", "-inPosition", MSyntax::kDouble, MSyntax::kDouble, MSyntax::kDouble);



   // OUTPUT/QUERYABLE FLAGS:

   syntax.addFlag("-p", "-position");

   syntax.addFlag("-nr", "-normal");

   syntax.addFlag("-t", "-tangent");

   syntax.addFlag("-u", "-paramU");

   syntax.addFlag("-d", "-distance");



   // DEFINE BEHAVIOUR OF COMMAND ARGUMENT THAT SPECIFIES THE CURVE NODE:

   syntax.useSelectionAsDefault(true);

   syntax.setObjectType(MSyntax::kSelectionList, 1, 1);



   // MAKE COMMAND QUERYABLE AND NON-EDITABLE:

   syntax.enableQuery(true);

   syntax.enableEdit(false);



   return syntax;

}





// MAKE THIS COMMAND UNDOABLE:

bool closestPointOnCurveCommand::isUndoable() const

{

   return true;

}





// PARSE THE COMMAND'S FLAGS AND ARGUMENTS AND STORING THEM AS PRIVATE DATA, THEN DO THE WORK BY CALLING redoIt():

MStatus closestPointOnCurveCommand::doIt(const MArgList& args)

{

   // CREATE THE PARSER:

   MArgDatabase argData(syntax(), args);



   // STORE FLAG INDICATORS, STORING WHETHER EACH FLAG HAS BEEN SET OR NOT:

   queryFlagSet = argData.isQuery();

   inPositionFlagSet = argData.isFlagSet("inPosition");

   positionFlagSet = argData.isFlagSet("-position");

   normalFlagSet = argData.isFlagSet("-normal");

   tangentFlagSet = argData.isFlagSet("-tangent");

   paramUFlagSet = argData.isFlagSet("-paramU");

   distanceFlagSet = argData.isFlagSet("-distance");



   // STORE THE NAME OF THE "closestPointOnCurve" NODE IF SPECIFIED, OTHERWISE ASSIGN DEFAULT:

   if (argData.isFlagSet("-name"))

      argData.getFlagArgument("-name", 0, closestPointOnCurveNodeName);

   else

      closestPointOnCurveNodeName = "";



   // STORE THE "inPosition" IF SPECIFIED, OTHERWISE ASSIGN DEFAULT:

   if (inPositionFlagSet)

   {

      argData.getFlagArgument("-inPosition", 0, inPosition.x);

      argData.getFlagArgument("-inPosition", 1, inPosition.y);

      argData.getFlagArgument("-inPosition", 2, inPosition.z);

   }

   else

   {

      inPosition.x = 0.0;

      inPosition.y = 0.0;

      inPosition.z = 0.0;

   }



   // STORE THE SPECIFIED OBJECT, INPUTTED FROM EITHER THE COMMAND ARGUMENT OR CURRENT SELECTION:

   argData.getObjects(sList);



   return redoIt();

}





// COMPUTING THE OUTPUT VALUES FOR THE CLOSEST POSITION, NORMAL, TANGENT, PARAMETER-U AND DISTANCE, OR CREATING A "closestPointOnCurve" NODE:

MStatus closestPointOnCurveCommand::redoIt()

{

   // DOUBLE-CHECK TO MAKE SURE THERE'S A SPECIFIED OBJECT TO EVALUATE ON:

   if (sList.length() == 0)

   {

      displayError("A curve or its transform node must be specified as a command argument, or using your current selection.");

      return MStatus::kFailure;

   }



   // RETRIEVE THE SPECIFIED OBJECT AS A DAGPATH:

   MDagPath curveDagPath;

   sList.getDagPath(0, curveDagPath);



   // CHECK FOR INVALID NODE-TYPE INPUT WHEN SPECIFIED/SELECTED NODE IS *NOT* A "CURVE" NOR "CURVE TRANSFORM":

   if (!curveDagPath.node().hasFn(MFn::kNurbsCurve) && !(curveDagPath.node().hasFn(MFn::kTransform) && curveDagPath.hasFn(MFn::kNurbsCurve)))

   {

      displayError("Invalid type!  Only a curve or its transform can be specified!");

      return MStatus::kFailure;

   }



   // WHEN COMMAND *NOT* IN "QUERY MODE" (I.E. "CREATION MODE"), CREATE AND CONNECT A "closestPointOnCurve" NODE AND RETURN ITS NODE NAME:

   if (!queryFlagSet)

   {

      // CREATE THE NODE:

      MFnDependencyNode depNodeFn;

      if (closestPointOnCurveNodeName == "")

         depNodeFn.create("closestPointOnCurve");

      else

         depNodeFn.create("closestPointOnCurve", closestPointOnCurveNodeName);

      closestPointOnCurveNodeName = depNodeFn.name();



      // SET THE ".inPosition" ATTRIBUTE, IF SPECIFIED IN THE COMMAND:

      if (inPositionFlagSet)

      {

         MPlug inPositionXPlug = depNodeFn.findPlug("inPositionX");

         inPositionXPlug.setValue(inPosition.x);

         MPlug inPositionYPlug = depNodeFn.findPlug("inPositionY");

         inPositionYPlug.setValue(inPosition.y);

         MPlug inPositionZPlug = depNodeFn.findPlug("inPositionZ");

         inPositionZPlug.setValue(inPosition.z);

      }



      // MAKE SOME ADJUSTMENTS WHEN THE SPECIFIED NODE IS A "TRANSFORM" OF A CURVE SHAPE:

      unsigned instanceNumber=0;

      if (curveDagPath.node().hasFn(MFn::kTransform))

      {

         // EXTEND THE DAGPATH TO ITS CURVE "SHAPE" NODE:

         curveDagPath.extendToShape();



         // TRANSFORMS ARE *NOT* NECESSARILY THE "FIRST" INSTANCE TRANSFORM OF A CURVE SHAPE:

         instanceNumber = curveDagPath.instanceNumber();

      }



      // CONNECT THE NODES:

      MPlug worldCurvePlug, inCurvePlug;

      inCurvePlug = depNodeFn.findPlug("inCurve");

      depNodeFn.setObject(curveDagPath.node());

      worldCurvePlug = depNodeFn.findPlug("worldSpace");

      worldCurvePlug = worldCurvePlug.elementByLogicalIndex(instanceNumber);

      MDGModifier dgModifier;

      dgModifier.connect(worldCurvePlug, inCurvePlug);

      dgModifier.doIt();



      // SET COMMAND RESULT TO BE NEW NODE'S NAME, AND RETURN:

      setResult(closestPointOnCurveNodeName);

      return MStatus::kSuccess;

   }

   // OTHERWISE, WE'RE IN THE COMMAND'S "QUERY MODE":

   else

   {

      // COMPUTE THE CLOSEST POSITION, NORMAL, TANGENT, PARAMETER-U AND DISTANCE, USING THE *FIRST* INSTANCE TRANSFORM WHEN CURVE IS SPECIFIED AS A "SHAPE":

      MPoint position;

      MVector normal, tangent;

      double paramU, distance;

      closestTangentUAndDistance(curveDagPath, inPosition, position, normal, tangent, paramU, distance);



      // WHEN NO QUERYABLE FLAG IS SPECIFIED, INDICATE AN ERROR:

      if (!positionFlagSet && !normalFlagSet && !tangentFlagSet && !paramUFlagSet && !distanceFlagSet)

      {

         displayError("You must specify AT LEAST ONE queryable flag in query mode.  Use the `help` command to list all available flags.");

         return MStatus::kFailure;

      }

      // WHEN JUST THE "DISTANCE" IS QUERIED, RETURN A SINGLE "FLOAT" INSTEAD OF AN ENTIRE FLOAT ARRAY FROM THE COMMAND:

      else if (distanceFlagSet && !(positionFlagSet || normalFlagSet || tangentFlagSet || paramUFlagSet))

         setResult(distance);

      // WHEN JUST THE "PARAMETER-U" IS QUERIED, RETURN A SINGLE "FLOAT" INSTEAD OF AN ENTIRE FLOAT ARRAY FROM THE COMMAND:

      else if (paramUFlagSet && !(positionFlagSet || normalFlagSet || tangentFlagSet || distanceFlagSet))

         setResult(paramU);

      // OTHERWISE, SET THE RETURN VALUE OF THE COMMAND'S RESULT TO A "COMPOSITE ARRAY OF FLOATS":

      else

      {

         // HOLDS FLOAT ARRAY RESULT:

         MDoubleArray floatArrayResult;



         // APPEND THE RESULTS OF THE CLOSEST POSITION, NORMAL, TANGENT, PARAMETER-U AND DISTANCE VALUES TO THE FLOAT ARRAY RESULT:

         if (positionFlagSet)

         {

            floatArrayResult.append(position.x);

            floatArrayResult.append(position.y);

            floatArrayResult.append(position.z);

         }

         if (normalFlagSet)

         {

            floatArrayResult.append(normal.x);

            floatArrayResult.append(normal.y);

            floatArrayResult.append(normal.z);

         }

         if (tangentFlagSet)

         {

            floatArrayResult.append(tangent.x);

            floatArrayResult.append(tangent.y);

            floatArrayResult.append(tangent.z);

         }

         if (paramUFlagSet)

            floatArrayResult.append(paramU);

         if (distanceFlagSet)

            floatArrayResult.append(distance);



         // FINALLY, SET THE COMMAND'S RESULT:

         setResult(floatArrayResult);

      }



      return MStatus::kSuccess;

   }

}





// CALLED WHEN USER UNDOES THE COMMAND:

MStatus closestPointOnCurveCommand::undoIt()

{

   // MERELY DELETE THE "closestPointOnCurve" NODE THAT WAS CREATED, IF ONE WAS CREATED:

   if (!queryFlagSet)

   {

      MString deleteCmd = "delete ";

      deleteCmd += closestPointOnCurveNodeName;

      MGlobal::executeCommand(deleteCmd);

   }



   return MStatus::kSuccess;

}

